package com.uinnova.product.eam.service.dm.impl;

import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.dm.DataModelAttribute;
import com.uinnova.product.eam.model.dm.DataModelStaticName;
import com.uinnova.product.eam.model.dm.bean.AttrAndCiDto;
import com.uinnova.product.eam.model.dm.bean.DataStandardTypeDto;
import com.uinnova.product.eam.model.dm.bean.EntityParamDto;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.dm.DataModelQuerySvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.ESCIClassInfo;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.util.sys.SysUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wcl
 */

@Service
public class DataModelQuerySvcImpl implements DataModelQuerySvc {

    @Resource
    private IRltClassApiSvc rltClassApiSvc;

    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;

    @Resource
    private ICISwitchSvc iciSwitchSvc;

    @Resource
    private IamsCIRltSwitchSvc rltSwitchSvc;

    @Override
    public Page<AttrAndCiDto> selectEntityAttrList(EntityParamDto dto) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        LibType libType = dto.getLibType();
        String classCode = getEntityCode(dto.getClassName());
        Page<AttrAndCiDto> result = new Page<>(dto.getPageNum(), dto.getPageSize(), 0, 0, new ArrayList<>());
        //根据分类的classCode查到分类信息，进而拿到分类id(classId)
        ESCIClassInfo entityClass = ciClassApiSvc.getCIClassByCode(classCode);
        if (BinaryUtils.isEmpty(entityClass) || BinaryUtils.isEmpty(entityClass.getAttrDefs())) {
            return result;
        }
        ESCIClassInfo attrClass = ciClassApiSvc.getCIClassByCode(Env.ATTRIBUTES.getCode());
        ESCIClassInfo standClass = ciClassApiSvc.getCIClassByCode(Env.STANDARD.getCode());
        //全量实体信息
        ESCISearchBean bean = EamUtil.copy(dto, ESCISearchBean.class);
        bean.setDomainId(domainId);
        bean.setClassIds(Collections.singletonList(entityClass.getId()));
        if(!BinaryUtils.isEmpty(dto.getWord())){
            bean.setWords(Collections.singletonList(dto.getWord()));
        }
        Page<ESCIInfo> dataPage = iciSwitchSvc.searchESCIByBean(bean, dto.getLibType());
        result.setTotalRows(dataPage.getTotalRows());
        result.setTotalPages(dataPage.getTotalPages());
        List<ESCIInfo> data = dataPage.getData();
        if (BinaryUtils.isEmpty(data)) {
            return result;
        }
        for (ESCIInfo info : data) {
            info.setShape(entityClass.getShape());
        }
        //封装返回体
        List<AttrAndCiDto> dtoList = new ArrayList<>();
        //根据实体ciCode 重新组合关系数据成map
        Map<String, List<CcCiInfo>> entityGroup = queryEntityAttrs(data, entityClass.getId(), attrClass.getId(), dto.getOwnerCode(), dto.getLibType());
        List<ESCIInfo> entityData = data.stream().sorted(Comparator.comparing(ESCIInfo::getModifyTime).reversed()).collect(Collectors.toList());
        for (ESCIInfo datum : entityData) {
            String entityCiCode = datum.getCiCode();
            if (BinaryUtils.isEmpty(entityCiCode)) {
                continue;
            }
            //拿实体ciCode匹配
            List<CcCiInfo> attrList = entityGroup.getOrDefault(entityCiCode, Lists.newArrayList());
            AttrAndCiDto ciDto = new AttrAndCiDto();
            ciDto.setEntityCi(datum);
            ciDto.setEntityAttrDefs(entityClass.getCcAttrDefs());
            ciDto.getEntityCi().setShape(entityClass.getShape());
            List<ESCIInfo> attrCiList = new ArrayList<>();
            for (CcCiInfo attrCiInfo : attrList) {
                ESCIInfo esciInfo = EamUtil.coverCiInfo(attrCiInfo);
                esciInfo.setShape(attrClass.getShape());
                Object standard = esciInfo.getAttrs().get("数据标准");
                if(!BinaryUtils.isEmpty(standard)){
                    esciInfo.setShape(standClass.getShape());
                }
                attrCiList.add(esciInfo);
            }
            ciDto.setAttrAttrDefs(attrClass.getCcAttrDefs());
            ciDto.setAttrCiList(attrCiList);
            ciDto.setCount(attrList.size());
            dtoList.add(ciDto);
        }
        result.setData(dtoList);
        return result;
    }

    /**
     * 获取实体分类标识
     * @param className 实体分类名称
     * @return 分类标识
     */
    private String getEntityCode(String className){
        if (className.equals(Env.CONCEPTION_ENTITY.getName())) {
            return Env.CONCEPTION_ENTITY.getCode();
        }else if (className.equals(Env.LOGIC_ENTITY.getName())) {
            return Env.LOGIC_ENTITY.getCode();
        }else if (className.equals(Env.PHYSICAL_ENTITY.getName())) {
            return Env.PHYSICAL_ENTITY.getCode();
        }
        return null;
    }

    /**
     * 根据关系查询实体包含的属性
     * @param data 实体集合
     * @param entityClassId 实体分类id
     * @param attrClassId 实体属性分类id
     * @return 实体包含实体属性map
     */
    public Map<String, List<CcCiInfo>> queryEntityAttrs(List<ESCIInfo> data, Long entityClassId, Long attrClassId, String ownerCode, LibType libType){
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        Map<String, List<CcCiInfo>> result = new HashMap<>(16);
        //根据源端关系信息查出目标端信息----进行数量展示
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(domainId, Env.DM_INCLUDE);
        if (BinaryUtils.isEmpty(rltClass)) {
            //没有属性分类的话，直接返回实体数据，属性数量都为0
            return result;
        }
        ESRltSearchBean rltSearchBean = new ESRltSearchBean();
        if (LibType.PRIVATE.equals(libType)) {
            rltSearchBean.setOwnerCode(ownerCode);
        }
        Set<String> sourceCiCodes = data.stream().map(CcCi::getCiCode).collect(Collectors.toSet());
        rltSearchBean.setSourceCiCodes(sourceCiCodes);
        rltSearchBean.setRltClassIds(Lists.newArrayList(rltClass.getCiClass().getId()));
        rltSearchBean.setSourceClassIds(Collections.singletonList(entityClassId));
        rltSearchBean.setTargetClassIds(Collections.singletonList(attrClassId));
        //根据实体ciCode,查出对应得关系数据，取出所有得目标端 属性信息进行组合；
        List<CcCiRltInfo> rltList = rltSwitchSvc.searchRltByScroll(rltSearchBean, libType);
        //当关系数据不存在时，将实体的个数设置成0；
        if (BinaryUtils.isEmpty(rltList)) {
            return result;
        }
        //根据实体ciCode 重新组合关系数据成map
        for (CcCiRltInfo rltInfo : rltList) {
            String entityCode = rltInfo.getCiRlt().getSourceCiCode();
            result.putIfAbsent(entityCode, new ArrayList<>());
            result.get(entityCode).add(rltInfo.getTargetCiInfo());
        }
        return result;
    }

    @Override
    public List<DataStandardTypeDto> selectDataStandardList(String ident, LibType libType) {
        CcCiClassInfo ciClass = new CcCiClassInfo();
        CcCiClassInfo attrCiClass = new CcCiClassInfo();
        String key = null;
        //查询数据标准
        if("standard".equals(ident)){
            //数据标准分类
            ciClass = ciClassApiSvc.getCIClassByCodes(Env.STANDARD_CLASS.getCode());
            //数据标准
            attrCiClass = ciClassApiSvc.getCIClassByCodes(Env.STANDARD.getCode());
            //所属分类
           key = DataModelAttribute.BELONG_CLASS;
        }
        //查询值域
        if("domainClass".equals(ident)){
            //域组
            ciClass = ciClassApiSvc.getCIClassByCodes(Env.DOMAIN_CLASS.getCode());
            //值域
            attrCiClass = ciClassApiSvc.getCIClassByCodes(Env.DoMAIN.getCode());
            //所属域组
            key = DataModelAttribute.BELONG_DOMAIN;
        }
        if(BinaryUtils.isEmpty(ciClass) || ciClass.getCiClass() == null){
            return Lists.newArrayList();
        }
        Long classId = ciClass.getCiClass().getId();
        List<ESCIInfo> data = getCiInfos(libType, classId);
        //父级分类为key，把有值的下一级待查的域组查出来
        Map<String, List<ESCIInfo>> parentCiCodeMap = data.stream().filter(ciInfo -> ciInfo.getAttrs().get(DataModelStaticName.PARENT) != null).collect(Collectors.groupingBy(ciInfo -> ciInfo.getAttrs().get(DataModelStaticName.PARENT).toString()));
        List<ESCIInfo> ccCis = getCiInfos(libType, attrCiClass.getCiClass().getId());
        //数据标准:key = 所属父级的  ciCode
        String finalKey = key;
        Map<String, List<ESCIInfo>> standMap = ccCis.stream().filter(ccCi -> ccCi.getAttrs().get(finalKey) != null).collect(Collectors.groupingBy(ccCi -> ccCi.getAttrs().get(finalKey).toString()));
        String topCode = "0";
        return getChildType(parentCiCodeMap.getOrDefault(topCode, Lists.newArrayList()), parentCiCodeMap, standMap, attrCiClass,ident);
    }

    /***
     *
     * @param libType 私有库/设计库
     * @param classId 分类id
     * @return ci的条件查询结果
     */
    public List<ESCIInfo> getCiInfos(LibType libType, Long classId) {
        ESCISearchBean bean = new ESCISearchBean();
        CCcCi cCi = new CCcCi();
        cCi.setClassId(classId);
        bean.setCdt(cCi);
        bean.setPageNum(1);
        bean.setPageSize(10000);
        return iciSwitchSvc.searchESCIByBean(bean, libType).getData();
    }

    /***
     * 获取子级目录及数据集合
     * @param ciList 目录ci集合
     * @param parentCiCodeMap 查询的数据标准类型的map信息
     * @param standMap 数据标准信息
     * @return 子层级信息
     */
    public List<DataStandardTypeDto> getChildType(List<ESCIInfo> ciList, Map<String, List<ESCIInfo>> parentCiCodeMap, Map<String, List<ESCIInfo>> standMap, CcCiClassInfo attrCiClass,String ident) {
        List<DataStandardTypeDto> result = new ArrayList<>();
        String className = null;
        if("standard".equals(ident)){
            className = DataModelStaticName.CLASS_NAME;
        }
        if("domainClass".equals(ident)){
            className = DataModelStaticName.CNAME;
        }
        for (ESCIInfo ci : ciList) {
            DataStandardTypeDto dir = new DataStandardTypeDto();
            dir.setIsDir(true);
            dir.setCiCode(ci.getCiCode());
            dir.setStandClassName(ci.getAttrs().get(className).toString());
            dir.setParentCode(ci.getAttrs().get(DataModelStaticName.PARENT).toString());
            dir.setLvl(Integer.parseInt(ci.getAttrs().get(DataModelStaticName.LVL).toString()));
            List<ESCIInfo> standardList = standMap.getOrDefault(ci.getCiCode(), Lists.newArrayList());
            dir.setCount(standardList.size());
            List<DataStandardTypeDto> dataList = EamUtil.copy(standardList, DataStandardTypeDto.class);
            dataList.forEach(each -> {
                each.setAttrDefs(attrCiClass.getAttrDefs());
                each.setShape(attrCiClass.getCiClass().getShape());
            });
            List<ESCIInfo> childList = parentCiCodeMap.getOrDefault(ci.getCiCode(), Lists.newArrayList());
            List<DataStandardTypeDto> dirList = getChildType(childList, parentCiCodeMap, standMap, attrCiClass,ident);
            dirList.addAll(dataList);
            dir.setChildList(dirList);
            result.add(dir);
        }
        return result;
    }

}
